﻿using System;
using System.Collections.Generic;
using FibMatrix.Task;

namespace FibMatrix
{
    /// <summary>
    /// 每个异步任务经过流水线时都会有一个context，负责流水线上的流转，也会包含必要信息
    /// 如果同时有对同一资源的多个加载请求，会共用同一个context，并把请求都加到它上?
    /// </summary>
    [FM_Mono.IgnoreGen]
    public class ResourcePipelineContext : IResourcePipelineContext, IDisposable
    {
        public ResourcePipelineContext()
        {
        }

        public AbstractLoadItem item { get; set; }
        internal AssetActionLogger.ActionStartData startData;

        public Type type { get; private set; }

        private List<IResourceLoadPipe> _pipelines = null;

        private ResourceError _error = ResourceError.None;
        public ResourceError error { get { return this._error; } }

        private FMTask _currentTask = null;

        private int _currentPipeIndex = 0;

        private Action<ResourcePipelineContext, ResourceError, List<IResourcePipelineCallback>> _completeCallback;

        private List<IResourcePipelineCallback> _requests = null;

        public void Add(IResourcePipelineCallback request)
        {
            _requests = _requests ?? new List<IResourcePipelineCallback>(1);
            _requests.Add(request);
        }

        public void Remove(IResourcePipelineCallback request)
        {
            // TODO?
            if (_requests != null)
            {
                int index = _requests.IndexOf(request);
                if (index >= 0)
                {
                    _requests[index] = null;
                }
            }
        }

        public bool FlowIn(AbstractLoadItem item, Type type, List<IResourceLoadPipe> pipelines, 
            Action<ResourcePipelineContext, ResourceError, List<IResourcePipelineCallback>> completeCallback)
        {
            if (item == null) return false;
            this._pipelines = pipelines;
            this.item = item;
            this.type = type;
            this._completeCallback = completeCallback;

            return this.Flow(0);
        }

        private bool Flow(int pipeIndex)
        {
            if (pipeIndex >= _pipelines.Count)
            {
                return false;
            }
            var pipe = this._pipelines[pipeIndex];
            this._error = ResourceError.None;
            this._currentTask = pipe.Handle(this, out this._error);
            if (error != ResourceError.None)
            {
                if (this._currentTask != null)
                {
                    this._currentTask.Dispose();
                    this._currentTask = null;
                }
                return false;
            }
            else if (this._currentTask != null && this._currentTask.status != FMTask.Status.Working)
            {
                this._currentTask.Dispose();
                this._currentTask = null;
            }
            if (this._currentTask == null)
            {
                return this.Flow(pipeIndex + 1);
            }
            this._currentPipeIndex = pipeIndex;
            this._currentTask.onCompleted = this.OnTaskComplete;
            this._currentTask.onError = this.OnTaskError;
            return true;
        }

        private static List<IResourcePipelineCallback> _cacheRequests = new List<IResourcePipelineCallback>();

        private void OnTaskComplete(FMTask task)
        {
            this._currentTask = null;
            task.Dispose();
            if (!this.Flow(this._currentPipeIndex + 1))
            {
                DoCompleteCallback(task);
            }
        }

        private void OnTaskError(FMTask task, string errorDesc, int errorCode)
        {
            this._error = (ResourceError)errorCode;
            this._currentTask = null;
            task.Dispose();
            DoCompleteCallback(task);
        }

        private void DoCompleteCallback(FMTask task)
        {
            var callback = this._completeCallback;
            this._completeCallback = null;
            // 已有request移动到另一个地方暂存
            var requests = this._requests;
            if (requests != null && requests.Count > 0)
            {
                if (_cacheRequests.Count > 0)
                {
                    _cacheRequests.Clear();
                }
                _cacheRequests.AddRange(requests);
                requests.Clear();
                requests = _cacheRequests;
            }
            callback(this, this._error, requests);
            _cacheRequests.Clear();
        }

        public void Dispose()
        {
            this.item = null;
            this.type = null;
            this._completeCallback = null;
            this._currentPipeIndex = 0;
            FMTask task = this._currentTask;
            this._currentTask = null;
            if (task != null)
            {
                task.Dispose();
            }
        }
    }
}
